package serv

import (
	"d-serv-go/server/core/bean"
	"d-serv-go/server/core/consts"
	"d-serv-go/server/core/db"
	"d-serv-go/server/core/log"
	"d-serv-go/server/core/util"
	"errors"
	"fmt"
	"github.com/kirinlabs/utils/str"
)

type servDao struct{}

func NewServDao() *servDao {
	return &servDao{}
}

func (ctl *servDao) Create(servId string, dataBean *db.ParamBean) (*bean.Bean, error) {
	serv, err := ServUtils.GetServBean(servId)
	context := db.Ctx()
	if err != nil {
		return nil, errors.New("没有找到服务定义：" + servId)
	}
	createBean := dataBean.Clone() //复制一份确保参数信息不会被修改
	preValue := []interface{}{}
	psql, values := SqlBuilder.Insert(serv, createBean, preValue)
	preValue = append(preValue, values...)
	//进行唯一组约束的判断
	uniqueStr := ServUtils.CheckUniqueExists(serv, createBean, true)
	if len(uniqueStr) > 0 {
		return nil, errors.New("存在唯一约束冲突,PK:" + uniqueStr)
	}
	num, err := context.GetExecutor().Execute(psql, preValue)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("servctl.create出现异常:%v", err))
	}
	if num > 0 {
		createBean.SetId(createBean.GetStr(serv.GetPKey())) //设置主键字段
		return createBean, nil
	}
	return nil, nil
}

func (ctl *servDao) Update(servId string, dataBean *db.ParamBean) (*bean.Bean, error) {
	servDef, err := ServUtils.GetServBean(servId)
	if err != nil {
		return nil, err
	}
	context := db.Ctx()
	updateBean := dataBean.Copy() //复制一份参数确保原参数不被修改
	updateBean.Set("S_MTIME", util.DateUtil.GetDatetimeTS())
	preValue := GetPreValueClone(db.NewParamBean(updateBean))
	psql, values, _ := SqlBuilder.Update(servDef, updateBean, dataBean, preValue)
	num, err := context.GetExecutor().Execute(psql, values)
	if num > 0 {
		key := servDef.GetPKey()
		if updateBean.Contains(key) { //设置主键字段
			updateBean.SetId(updateBean.GetStr(key))
		}
		servDef.ClearDataCache(updateBean.GetId()) //清除缓存
		return updateBean, nil
	} else {
		return nil, err
	}
}

func (ctl *servDao) Save(servId string, dataBean *db.ParamBean) (*bean.Bean, error) {
	if len(dataBean.GetId()) > 0 {
		return ctl.Update(servId, dataBean)
	} else {
		return ctl.Create(servId, dataBean)
	}
}

func (ctl *servDao) Updates(servId string, setBean *bean.Bean, whereBean *db.ParamBean) (int64, error) {
	serv, err := ServUtils.GetServBean(servId)
	if err != nil {
		return 0, errors.New("没有找到服务定义：" + servId)
	}
	context := db.Ctx()
	preValue := GetPreValueClone(whereBean)
	psql, values, err := SqlBuilder.Update(serv, setBean, whereBean, preValue)
	if err != nil {
		return 0, err
	}
	preValue = append(preValue, values)
	count, err := context.GetExecutor().Execute(psql, preValue)
	if count > 0 { //清除缓存
		serv.ClearDataCache("")
	}
	return count, nil
}

func GetPreValueClone(dataBean *db.ParamBean) []interface{} {
	if dataBean.Contains(consts.PARAM_PRE_VALUES) {
		// TODO 这里缺少个clone
		return dataBean.GetArray(consts.PARAM_PRE_VALUES)
	}
	return nil
}

func (ctl *servDao) FindById(servId string, id string, withLinks bool) (*bean.Bean, error) {
	paramBean := db.NewParamBean()
	paramBean.SetId(id)
	paramBean.Set(consts.PARAM_LINK_FLAG, withLinks)
	return ctl.Find1(servId, paramBean)
}

func (ctl *servDao) Find1(servId string, paramBean *db.ParamBean) (*bean.Bean, error) {
	var result *bean.Bean
	context := db.Ctx()
	withLinks := paramBean.GetBool(consts.PARAM_LINK_FLAG)
	servDef, _ := ServUtils.GetServBean(servId)
	if !withLinks && servDef.HasCache() {
		dataId := paramBean.GetId()
		result = servDef.GetDataCache(dataId)
	}
	if result == nil {
		preValue := GetPreValueClone(paramBean)
		psql, values := SqlBuilder.Select(servDef, paramBean)
		preValue = append(preValue, values...)
		if len(paramBean.GetId()) > 0 { //明确了基于主键查询
			result, _ = context.GetExecutor().QueryById(psql, preValue)
		} else { //非主键查询限定只返回一条
			result, _ = context.GetExecutor().QueryOne(psql, preValue)
		}
		if result != nil {
			if withLinks {
				//获取关联数据，并设置到结果对象中
				links := servDef.GetAllLinks()
				for _, key := range links.Keys() {
					link := links.GetBean(key)
					if link.GetInt("LINK_SHOW_TYPE") == consts.LINK_SHOW_TYPE_URL { //url关联不获取数据
						continue
					}
					if !link.IsEmpty("LINK_EXPRESSION") { //处理表达式，禁止不符合规则的数据获取
						if !util.Lang.IsTrueScript(ServUtils.ReplaceSysAndData(link.GetStr("LINK_EXPRESSION"), result)) { //表达式不为true，则不允许获取数据
							continue
						}
					}
					dataList := ServUtils.GetLinkDataList(servId, link, result, 1)
					result.Set(link.GetStr("LINK_SERV_ID"), dataList)
				}
			} else if servDef.HasCache() {
				if str.StartsWith(servDef.GetStr("SERV_ID"), "SY_ORG_USER") {
					log.Info("setDataCache:服务编码:{},result:{}", servDef.GetStr("SERV_ID"), result)
				}
				servDef.SetDataCache(result.GetId(), result) //设置缓存
			}
		}
	}
	return result, nil
}

func (ctl *servDao) Finds(servId string, paramBean *db.ParamBean) ([]*bean.Bean, error) {
	call := func(columns []*bean.Bean, data *bean.Bean) int {
		return 1
	}
	return ctl.FindsCall2(servId, paramBean, call)
}

func (ctl *servDao) FindsCall2(servId string, paramBean *db.ParamBean, call db.Callback) ([]*bean.Bean, error) {
	context := db.Ctx()
	serv, _ := ServUtils.GetServBean(servId)
	preValue := GetPreValueClone(paramBean)
	psql, vals := SqlBuilder.Select(serv, paramBean)
	if len(vals) > 0 {
		preValue = append(preValue, vals...)
	}
	rowNum := paramBean.GetInt(consts.PARAM_ROWNUM, -1) //兼容以前方法，不建议使用此参数
	rowNum = paramBean.GetInt(consts.PAGE_SHOWNUM, rowNum)
	page := paramBean.GetInt(consts.PAGE_NOWPAGE, 1)
	start := (page - 1) * rowNum
	return context.GetExecutor().QueryPage(psql, start, rowNum, preValue, call)
}

func (ctl *servDao) Count(servId string, paramBean *db.SqlBean) (int, error) {
	context := db.Ctx()
	var param = db.NewParamBean(paramBean.Bean)
	serv, _ := ServUtils.GetServBean(servId)
	param.Set(consts.PARAM_SELECT, " count(*) COUNT_ ")
	preValue := GetPreValueClone(param)
	psql, values := SqlBuilder.Select(serv, param)
	preValue = append(preValue, values...)
	return context.GetExecutor().Count(psql, preValue)
}
